use anyhow::bail;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::Deserialize;

#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)]
#[serde(try_from="String")]
pub struct Key{
    pub code : KeyCode ,
    pub shift : bool ,
    pub ctrl: bool ,
    pub alt : bool ,
}

impl Default for Key{
    fn default() -> Self {
        Self {
            code:KeyCode::Null , 
            shift: false ,
            ctrl : false ,
            alt: false ,
        }
    }
}
impl From<KeyEvent> for Key {
    fn from(value: KeyEvent) -> Self {
        let shift = if let KeyCode::Char(c) = value.code { c.is_ascii_uppercase() }else {false };
        Self{
            code : value.code , 
            shift : shift || value.modifiers.contains(KeyModifiers::SHIFT) ,
            ctrl: value.modifiers.contains(KeyModifiers::CONTROL) , 
            alt : value.modifiers.contains(KeyModifiers::ALT) ,
        }
    }
}
impl TryFrom<String> for Key{
    type Error = anyhow::Error;
    fn try_from(s: String) -> Result<Self, Self::Error> {
        if s.is_empty() {
            bail!("empty key")
        }

        let mut key = Self::default();
        if !s.starts_with("<") || !s.ends_with(">") {
            let c = s.chars().next().unwrap();
            key.code = KeyCode::Char(c);
            key.shift = c.is_ascii_uppercase();
            return Ok(key);
        }

        let mut it = s[1..s.len() - 1].split_inclusive('-').peekable();
        while let Some(x) = it.next() {
            match x {
                "S-" => key.shift = true,
                "C-" => key.ctrl = true,
                "A-" => key.alt = true,

                "Space" => key.code = KeyCode::Char(' '),
                "Backspace" => key.code = KeyCode::Backspace,
                "Enter" => key.code = KeyCode::Enter,
                "Left" => key.code = KeyCode::Left,
                "Right" => key.code = KeyCode::Right,
                "Up" => key.code = KeyCode::Up,
                "Down" => key.code = KeyCode::Down,
                "Home" => key.code = KeyCode::Home,
                "End" => key.code = KeyCode::End,
                "PageUp" => key.code = KeyCode::PageUp,
                "PageDown" => key.code = KeyCode::PageDown,
                "Tab" => key.code = KeyCode::Tab,
                "Delete" => key.code = KeyCode::Delete,
                "Insert" => key.code = KeyCode::Insert,
                "F1" => key.code = KeyCode::F(1),
                "F2" => key.code = KeyCode::F(2),
                "F3" => key.code = KeyCode::F(3),
                "F4" => key.code = KeyCode::F(4),
                "F5" => key.code = KeyCode::F(5),
                "F6" => key.code = KeyCode::F(6),
                "F7" => key.code = KeyCode::F(7),
                "F8" => key.code = KeyCode::F(8),
                "F9" => key.code = KeyCode::F(9),
                "F10" => key.code = KeyCode::F(10),
                "F11" => key.code = KeyCode::F(11),
                "F12" => key.code = KeyCode::F(12),
                "Esc" => key.code = KeyCode::Esc,

                c if it.peek().is_none() => {
                    key.code = KeyCode::Char(c.chars().next().unwrap());
                }
                k => bail!("unknown key: {}", k),
            }
        }

        if key.code == KeyCode::Null {
            bail!("empty key")
        }
        Ok(key)
    }
}